/*
 * ============================================================================
 *
 *  Zombie:Reloaded
 *
 *  File:          napalm.inc
 *  Type:          Module 
 *  Description:   Grenades burn zombies when damaged by them.
 *
 *  Copyright (C) 2009  Greyscale, Richard Helgeby
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * ============================================================================
 */

/**
 * The fuse length of an hegrenade.
 */ 
#define GRENADE_FUSE_TIME 3.0

/**
 * @section m_nWaterLevel defines.
 */
#define NAPALM_WLEVEL_DRY 0
#define NAPALM_WLEVEL_FEET 1
#define NAPALM_WLEVEL_HALF 2
#define NAPALM_WLEVEL_FULL 3
/**
 * @endsection
 */

/**
 * Variable to store water-level offset value.
 */
new g_iToolsWaterLevel;

/**
 * Find napalm-specific offsets here.
 */
NapalmOnOffsetsFound()
{
    // If offset "m_bInBuyZone" can't be found, then stop the plugin.
    g_iToolsWaterLevel = FindSendPropInfo("CBasePlayer", "m_nWaterLevel");
    if (g_iToolsWaterLevel == -1)
    {
        LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Napalm, "Offsets", "Offset \"CBaseEntity::m_nWaterLevel\" was not found.");
    }
}

/**
 * Hook: OnTakeDamage
 * Forwarded from the damage module to check if we should extinguish any flames.
 * 
 * @param client        The client index.
 * @param damagetype    The type of damage inflicted.
 * @return              Return ZRTools_Handled to stop the damage to client.
 *                      ZRTools_Continue to allow damage to client.
 *                      Return -1 to not return anything and let the damage module continue as usual.
 */
NapalmOnTakeDamage(client, damagetype)
{
    // Client was damaged by fire.
    if (damagetype & DMG_CSS_BURN)
    {
        // Only take action if it isn't disabled, or the option is valid.
        new douse = GetConVarInt(g_hCvarsList[CVAR_NAPALM_DOUSE]);
        
        if (douse > NAPALM_WLEVEL_DRY && douse <= NAPALM_WLEVEL_FULL)
        {
            // If the client water-level is equal or higher than the given, then we want to extinguish the flame.
            if (NapalmGetClientWaterLevel(client) >= douse)
            {
                // Put the fire out.
                
                //ExtinguishEntity(client);  <-- Don't use this.  Takes off the FL_ONFIRE flag, but flame doesn't get extinguished.
                
                // This works.
                new fire = GetEntPropEnt(client, Prop_Data, "m_hEffectEntity");
                if (IsValidEntity(fire))
                {
                    // Make sure the entity is a flame, so we can extinguish it.
                    decl String:classname[64];
                    GetEdictClassname(fire, classname, sizeof(classname));
                    if (StrEqual(classname, "entityflame", false))
                    {
                        SetEntPropFloat(fire, Prop_Data, "m_flLifetime", 0.0);
                    }
                    // Log what entity was in that property, for future reference.
                    else
                    {
                        LogEvent(false, LogType_Normal, LOG_GAME_EVENTS, LogModule_Napalm, "Napalm Douse", "Found unexpected entity in prop \"m_flLifetime\": \"%s\"", classname);
                    }
                }
                
                return _:ACTION_CONTINUE;
            }
        }
    }
    
    // Let the damage module continue as usual.
    return -1;
}

/**
 *  Client has been hurt.
 * 
 * @param client    The client index.
 * @param attacker  The attacker index.
 * @param weapon    The weapon name. 
 */
NapalmOnClientHurt(client, attacker, const String:weapon[])
{
    // If there's no attacker, then stop.
    if (!attacker)
    {
        return;
    }
    
    // If player isn't a zombie, then stop.
    if (!InfectIsClientInfected(client))
    {
        return;
    }
    
    // If napalm time is invalid or 0, then stop.
    new Float:napalm_time = ClassGetNapalmTime(client);
    if (napalm_time <= 0.0)
    {
        return;
    }
    
    // If the attacker can't throw napalm grenades, then stop.
    if (!ClassGetHasNapalm(attacker))
    {
        return;
    }
    
    // If weapon is a grenade, then ignite player.
    if (StrEqual(weapon, "hegrenade", false))
    {
        new bool:reset = GetConVarBool(g_hCvarsList[CVAR_NAPALM_TIME_RESET]);
        new flags = GetEntityFlags(client);
        
        if (reset || !(flags & FL_ONFIRE))
        {
            // This stops the fire before re-ignition.
            ExtinguishEntity(client);
            
            // Ignite client.
            IgniteEntity(client, napalm_time);
        }
    }
}

/**
 * Client has been killed.
 * 
 * @param client    The client index.
 */
NapalmOnClientDeath(client)
{
    // Extinguish any flames to stop burning sounds.
    ExtinguishEntity(client);
}

/**
 *  Weapon has been fired.
 *
 * @param client       The client index.
 * @param weapon       The weapon name.
 */
NapalmOnWeaponFire(client, const String:weapon[])
{
    // If grenade fire is disabled, then stop.
    new bool:napalmignite = GetConVarBool(g_hCvarsList[CVAR_NAPALM_IGNITE]);
    if (!napalmignite)
    {
        return;
    }
    
    // If human class can't throw napalm grenades, then stop.
    if (!ClassGetHasNapalm(client))
    {
        return;
    }
    
    // If weapon isn't a grenade, then stop.
    if (!StrEqual(weapon, "hegrenade", false))
    {
        return;
    }
    
    // Wait .1 seconds.
    CreateTimer(0.1, NapalmIgniteGrenade);
}

/**
 * Timer callback, ignite's all hegrenade projectiles.
 * 
 * @param timer     The timer handle.
 */   
public Action:NapalmIgniteGrenade(Handle:timer)
{
    decl String:classname[64];
    
    // Get max entities.
    new maxentities = GetMaxEntities();
    
    // x = entity index.
    for (new x = 0; x <= maxentities; x++)
    {
        // If entity is invalid, then stop.
        if(!IsValidEntity(x) || !IsValidEdict(x))
        {
            continue;
        }
        
        GetEdictClassname(x, classname, sizeof(classname));
        if(StrEqual(classname, "hegrenade_projectile"))
        {
            IgniteEntity(x, GRENADE_FUSE_TIME);
        }
    }
}

/**
 * Checks the current water-level on a client.
 * 
 * @param client    The client index.
 * @return          A NAPALM_WLEVEL_ define. 
 */
stock NapalmGetClientWaterLevel(client)
{
    // Return client's water-level.
    return GetEntData(client, g_iToolsWaterLevel);
}